package com.xzy.es.common.util;

import java.util.*;

/**
 * @author xzy
 * @date 2020-12-24 11:55
 * 说明：随机工具
 */
public class RandomUtils {

    private RandomUtils() {
    }

    public static final Random RANDOM = new Random();

    /**
     * 字母集
     */
    public static final char[] LETTER_SET = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
            'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z'};

    /**
     * 数字集
     */
    public static final char[] NUMBER_SET = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};

    /**
     * 随机决策
     *
     * @param probability - 返回值为true的概率
     * @return true or false
     */
    public static boolean randomDecision(double probability) {
        // Math.random()随机返回数值在0.0和1.0之间的double值。
        return Math.random() <= probability;
    }

    /**
     * 获取随机字符串
     *
     * @param source - 字符集
     * @param length - 字符串长度
     * @return - 随机字符串
     */
    public static String getRandomString(char[] source, int length) {
        StringBuilder randomStringBuilder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            randomStringBuilder.append(source[RANDOM.nextInt(source.length)]);
        }
        return randomStringBuilder.toString();
    }

    public static <E> String getRandomString(List<E> source, int length) {
        // 1.打乱字符集
        Collections.shuffle(source);

        // 2.随机抽取元素生成字符串
        StringBuilder randomStringBuilder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            randomStringBuilder.append(source.get(RANDOM.nextInt(source.size())));
        }
        return randomStringBuilder.toString();
    }

    /**
     * 带权重的随机决策——基于线性扫描
     * 注意：
     * 1. 时间复杂度O(n)，n = prizePool.length
     * 2. 将权重大的数据靠前，可以减少列表遍历的次数
     *
     * @param prizePool  - “奖品池”，即需要决策的数据
     * @param weightPool - “奖品权重”，即数据所占的权重（决定数据被选中的概率）
     * @param <E>        - 数据类型
     * @return - 随机抽中的“奖品”
     */
    public static <E> E randomDecisionWithWeight(E[] prizePool, int[] weightPool) {
        if (prizePool.length == 0 || prizePool.length != weightPool.length) {
            throw new IllegalArgumentException();
        }

        /*
         * 奖品池、奖品权重：
         *
         *      A      B           C                       D
         *   |-----|-------|---------------|-------------------------------|
         *    1/15    2/15        4/15                    8/15
         *
         *  生成一个[0,15)区间内的随机数x，根据x所处的子区间决定抽取到的奖品：
         *                                     x
         *  |----------------------------------|
         */

        // 计算总权重，确定随机数生成范围
        int weightSum = 0;
        for (int weight : weightPool) {
            if (weight < 0) {
                throw new IllegalArgumentException("权重不允许是负数！");
            }
            weightSum += weight;
        }

        // 抽取数据
        int randomPrizePoint = new Random().nextInt(weightSum);
        E randomPrize = null;
        for (int i = 0; i < weightPool.length; i++) {
            if (randomPrizePoint < weightPool[i]) {
                randomPrize = prizePool[i];
                break;
            } else {
                randomPrizePoint -= weightPool[i];
            }
        }

        return randomPrize;
    }

    public static void main(String[] args) {
        String[] prizePool = new String[]{"一等奖", "二等奖", "三等奖", "鼓励奖"};
        int[] prizeWeight = new int[]{1, 2, 4, 8};

        Map<String, Integer> count = new HashMap<>(4);
        for (int i = 0; i < 1000; i++) {
            String randomPrize = randomDecisionWithWeight(prizePool, prizeWeight);
            count.put(randomPrize, count.getOrDefault(randomPrize, 0) + 1);
            System.out.println(randomPrize);
        }

        System.out.println("统计：" + count);
    }
}
